package com.zhz.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhz.config.IdAutoConfiguration;
import com.zhz.domain.Sms;
import com.zhz.domain.UserAuthAuditRecord;
import com.zhz.domain.UserAuthInfo;
import com.zhz.dto.UserDto;
import com.zhz.geetest.GeetestLib;
import com.zhz.mappers.UserDtoMapper;
import com.zhz.model.*;
import com.zhz.service.SmsService;
import com.zhz.service.UserAuthAuditRecordService;
import com.zhz.service.UserAuthInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhz.mapper.UserMapper;
import com.zhz.domain.User;
import com.zhz.service.UserService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author ：zhz
 * @date ：Created in 2021/01/02
 * @version: V1.0
 * @slogan: 天下风云出我辈，一入代码岁月催
 * @description:
 **/
@Service
@Slf4j

public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserAuthAuditRecordService userAuthAuditRecordService;

    @Autowired
    private UserAuthInfoService userAuthInfoService;

    @Autowired
    private GeetestLib geetestLib;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private Snowflake snowflake;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private SmsService smsService;

    /**
     * 条件查询会员信息
     *
     * @param page     分页信息
     * @param mobile   手机号
     * @param userId   会员id
     * @param userName 会员名称
     * @param realName 会员真实姓名
     * @param status   会员状态
     * @return
     */
    @Override
    public Page<User> findByPage(Page<User> page, String mobile, Long userId, String userName, String realName, Integer status, Integer reviewsStatus) {
        return page(page, new LambdaQueryWrapper<User>()
                .like(!StringUtils.isEmpty(mobile), User::getMobile, mobile)
                .like(!StringUtils.isEmpty(userName), User::getUsername, userName)
                .like(!StringUtils.isEmpty(realName), User::getRealName, realName)
                .eq(userId != null, User::getId, userId)
                .eq(status != null, User::getStatus, status)
                .eq(reviewsStatus != null, User::getReviewsStatus, reviewsStatus)
        );
    }

    /**
     * 通过用户的 Id 查询该用户邀请的人员
     *
     * @param page   分页信息
     * @param userId 用户的id
     * @return
     */
    @Override
    public Page<User> findDirectInvitePage(Page<User> page, Long userId) {
        return page(page, new LambdaQueryWrapper<User>()
                .eq(User::getDirectInviteid, userId));
    }

    /**
     * 修改用户的审核状态
     *
     * @param id         用户id
     * @param authStatus 用户审核状态
     * @param authCode   用户审核唯一code
     * @param remark     备注
     */
    @Override
    @Transactional
    public void updateUserAuthStatus(Long id, Byte authStatus, Long authCode, String remark) {
        log.info("开始修改用户的审核状态,当前用户{},用户的审核状态{},图片的唯一code{}", id, authStatus, authCode);
        User user = getById(id);
        if (user != null) {
            user.setReviewsStatus(authStatus.intValue());// 审核的状态
            updateById(user);//修改用户状态
        }
        //从jwt中获取审核人的ID
        String userStr = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
        //userAuthAuditRecord设值
        UserAuthAuditRecord userAuthAuditRecord = new UserAuthAuditRecord()
                .setUserId(id)
                .setStatus(authStatus)
                .setAuthCode(authCode)
                .setAuditUserId(Long.valueOf(userStr))   // 审核人的ID
                .setAuditUserName("------")              // 审核人的名称 --> 远程调用admin-service ,没有事务
                .setRemark(remark);
        userAuthAuditRecordService.save(userAuthAuditRecord);
    }

    /**
     * 用户实名认证
     *
     * @param id           用户的id
     * @param userAuthForm 认证的数据
     * @return
     */
    @Override
    public boolean identifyVerify(Long id, UserAuthForm userAuthForm) {
        User user = getById(id);
        Assert.notNull(user, "认证的用户不存在");
        Byte authStatus = user.getAuthStatus();
        if (!authStatus.equals((byte) 0)) {
            throw new IllegalArgumentException("该用户已经认证成功");
        }
        //执行认证
        checkForm(userAuthForm);//极验
        //实名认证
        boolean check = IdAutoConfiguration.check(userAuthForm.getRealName(), userAuthForm.getIdCard());
        if (!check) {
            throw new IllegalArgumentException("该用户信息错误，请检查");
        }
        //设置用户的值
        user.setAuthtime(new Date());
        user.setAuthStatus((byte) 1);
        user.setRealName(userAuthForm.getRealName());
        user.setIdCard(userAuthForm.getIdCard());
        user.setIdCardType(userAuthForm.getIdCardType());
        return updateById(user);
    }

    private void checkForm(UserAuthForm userAuthForm) {
        userAuthForm.check(geetestLib, redisTemplate);
    }

    @Override
    public User getById(Serializable id) {
        User user = super.getById(id);
        if (StringUtils.isEmpty(user)) {
            throw new IllegalArgumentException("请输入正确的用户id");
        }
        Byte SeniorAuthStatus = null;//用户的高级认证状态，原因会员的高级认证状态 0 : 审核中  1 :通过 2 : 拒绝--(拒绝的理由) 3 :未填写
        String SeniorAuthDesc = "";//用户的高级认证未通过
        Integer reviewsStatus = user.getReviewsStatus();//审核状态,1通过,2拒绝,0,待审核
        if (reviewsStatus == null) {//为null时，代表用户的资料没有上传
            SeniorAuthStatus = 3;
            SeniorAuthDesc = "资料未填写";
        } else {
            switch (reviewsStatus) {
                case 0:
                    SeniorAuthStatus = 0;
                    SeniorAuthDesc = "等待审核";
                    break;
                case 1:
                    SeniorAuthStatus = 1;
                    SeniorAuthDesc = "审核通过";
                    break;
                case 2:
                    SeniorAuthStatus = 2;
                    //查询拒绝的原因===》审核记录里面
                    List<UserAuthAuditRecord> userAuthAuditRecordList = userAuthAuditRecordService.getUserAuthAuditRecordList(user.getId());
                    if (!CollectionUtils.isEmpty(userAuthAuditRecordList)) {
                        UserAuthAuditRecord userAuthAuditRecord = userAuthAuditRecordList.get(0);
                        SeniorAuthDesc = userAuthAuditRecord.getRemark();
                    }
                    break;
            }
        }
        user.setSeniorAuthStatus(SeniorAuthStatus);
        user.setSeniorAuthDesc(SeniorAuthDesc);
        return user;
    }

    /**
     * 用户 的高级认证
     *
     * @param id   用户的id
     * @param imgs 用户上传的图片地址
     */
    @Override
    @Transactional
    public void authUser(long id, List<String> imgs) {
        if (CollectionUtils.isEmpty(imgs)) {
            throw new IllegalArgumentException("用户的身份证信息为null");
        }
        User user = super.getById(id);
        if (user == null) {
            throw new IllegalArgumentException("请输入争取的用户ID");
        }
        long authCode = snowflake.nextId();//雪花算法
        List<UserAuthInfo> userAuthInfoList = new ArrayList<>(imgs.size());
        for (int i = 0; i < imgs.size(); i++) {
            String img = imgs.get(i);
            UserAuthInfo userAuthInfo = new UserAuthInfo();
            userAuthInfo.setImageUrl(img);
            userAuthInfo.setUserId(id);
            userAuthInfo.setSerialno(i + 1);  //按照属性排 1：正面 2：反面 3：手持
            userAuthInfo.setAuthCode(authCode);//   是一组身份信息的标识，3个图片为一组
            userAuthInfoList.add(userAuthInfo);
        }
        userAuthInfoService.saveBatch(userAuthInfoList); //批量插入数据
        user.setReviewsStatus(0);//等待审核
        updateById(user);//更新用户状态

    }

    /**
     * 修改用户的手机号码
     *
     * @param userId           用户的id
     * @param updatePhoneParam 修改用户的参数
     * @return
     */
    @Override
    public boolean updatePhone(Long userId, UpdatePhoneParam updatePhoneParam) {
        //1、使用userId查询用户
        User user = getById(userId);
        //2、验证旧手机号码
        String oldMobile = user.getMobile();  //旧手机号码
        String oldMobileCode = stringRedisTemplate.opsForValue().get("SMS:VERIFY_OLD_PHONE:" + oldMobile);
        if (!updatePhoneParam.getOldValidateCode().equals(oldMobileCode)) {
            throw new IllegalArgumentException("旧手机号的验证码错误");
        }
        //3、验证新手机号码
        String newPhoneCode = stringRedisTemplate.opsForValue().get("SMS:CHANGE_PHONE_VERIFY:" + updatePhoneParam.getNewMobilePhone());
        if (!updatePhoneParam.getValidateCode().equals(newPhoneCode)) {
            throw new IllegalArgumentException("新手机号的验证码错误");
        }
        //4、修改手机号码
        user.setMobile(updatePhoneParam.getNewMobilePhone());
        return updateById(user);
    }

    /**
     * 检查新的号码是否可用，若可用，则给该新手机号码发送验证码
     *
     * @param mobile      新的手机号码
     * @param countryCode 国家代码
     * @return
     */
    @Override
    public boolean checkNewPhone(String mobile, String countryCode) {
        //1、新的手机号，没有旧用户使用
        int count = count(new LambdaQueryWrapper<User>()
                .eq(User::getMobile, mobile)
                .eq(User::getCountryCode, countryCode)
        );
        if (count > 0) {//有用户占有这个手机号
            throw new IllegalArgumentException("该手机号已经被占用");
        }
        //2、向新的手机号发送验证码
        Sms sms = new Sms()
                .setMobile(mobile)
                .setCountryCode(countryCode)
                .setTemplateCode("CHANGE_PHONE_VERIFY");//模板代码--》校验手机号
        smsService.sendSms(sms);
        return false;
    }

    /**
     * 修改密码
     *
     * @param userId           要修改密码的用户
     * @param updateLoginParam 修改密码的参数
     * @return
     */
    @Override
    public boolean updateLoginPassword(Long userId, UpdateLoginParam updateLoginParam) {
        //1、查询之前的用户
        User user = getById(userId);
        if (StringUtils.isEmpty(user)) {
            throw new IllegalArgumentException("用户的id错误");
        }
        //获取旧的密码
        String oldPassword = updateLoginParam.getOldpassword();
        //2、检验之前的密码，需要进行加密，才能与数据库的密码进行比较
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(updateLoginParam.getOldpassword(), user.getPassword());
        if (!matches) {
            throw new IllegalArgumentException("用户的原始密码输入错误");
        }
        //3、检验手机的验证码
        String validateCode = updateLoginParam.getValidateCode();
        String phoneValidateCode = stringRedisTemplate.opsForValue().get("SMS:CHANGE_LOGIN_PWD_VERIFY:" + user.getMobile());
        if (!validateCode.equals(phoneValidateCode)) {
            throw new IllegalArgumentException("手机验证码错误");
        }
        //把加密后的密码放数据库中
        user.setPassword(bCryptPasswordEncoder.encode(updateLoginParam.getNewpassword()));
        return updateById(user);
    }

    /**
     * 修改交易密码
     *
     * @param userId           用户的id
     * @param updateLoginParam 修改交易密码的参数
     * @return
     */
    @Override
    public boolean updatePayPassword(Long userId, UpdateLoginParam updateLoginParam) {
        //1、查询之前的用户
        User user = getById(userId);
        if (StringUtils.isEmpty(user)) {
            throw new IllegalArgumentException("用户的id错误");
        }
        //获取旧的交易密码
        String oldPassword = updateLoginParam.getOldpassword();
        //2、检验之前的密码，需要进行加密，才能与数据库的密码进行比较
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(updateLoginParam.getOldpassword(), user.getPaypassword());
        if (!matches) {
            throw new IllegalArgumentException("用户的原始支付密码输入错误");
        }
        //3、检验手机的验证码
        String validateCode = updateLoginParam.getValidateCode();
        String phoneValidateCode = stringRedisTemplate.opsForValue().get("SMS:CHANGE_PAY_PWD_VERIFY:" + user.getMobile());
        if (!validateCode.equals(phoneValidateCode)) {
            throw new IllegalArgumentException("手机验证码错误");
        }
        user.setPaypassword(bCryptPasswordEncoder.encode(updateLoginParam.getNewpassword()));
        return updateById(user);
    }

    /**
     * 重置支付密码
     *
     * @param userId                用户id
     * @param unsetPayPasswordParam 重置密码的表单参数
     * @return 是否重置成功
     */
    @Override
    public boolean unsetPayPassword(Long userId, UnsetPayPasswordParam unsetPayPasswordParam) {
        User user = getById(userId);
        if (StringUtils.isEmpty(user)) {
            throw new IllegalArgumentException("用户的id错误");
        }
        String validateCode = unsetPayPasswordParam.getValidateCode();
        String phoneValidate = stringRedisTemplate.opsForValue().get("SMS:FORGOT_PAY_PWD_VERIFY:" + user.getMobile());
        if (!validateCode.equals(phoneValidate)) {
            throw new IllegalArgumentException("用户的验证码错误");
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        user.setPaypassword(bCryptPasswordEncoder.encode(unsetPayPasswordParam.getPayPassword()));
        return updateById(user);
    }

    /**
     * 获取该用户邀请的用户列表
     *
     * @param userId 用户的Id
     * @return
     */
    @Override
    public List<User> getUserInvites(Long userId) {
        List<User> list = list(new LambdaQueryWrapper<User>()
                .eq(User::getDirectInviteid, userId));
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        list.forEach(user -> {
            user.setPaypassword("*********");
            user.setPassword("*********");
            user.setAccessKeyId("*********");
            user.setAccessKeySecret("*********");
        });
        return list;
    }

    /**
     * 用户的注册
     *
     * @param registerParam 注册的表单参数
     * @return
     */
    @Override
    public boolean register(RegisterParam registerParam) {
        log.info("用户开始注册{}", JSON.toJSONString(registerParam, true));
        String mobile = registerParam.getMobile();
        String email = registerParam.getEmail();
        //1、检验参数是否合法
        if (StringUtils.isEmpty(mobile) && StringUtils.isEmpty(email)) {
            throw new IllegalArgumentException("手机号或邮箱不能同时为空");
        }
        //2、查询数据库进行校验,看看数据库中是否已经存在
        int count = count(new LambdaQueryWrapper<User>()
                .eq(!StringUtils.isEmpty(email), User::getEmail, email)
                .eq(!StringUtils.isEmpty(mobile), User::getMobile, mobile)
        );
        if (count > 0) {
            throw new IllegalArgumentException("手机号或邮箱已经被注册");
        }
        //滑动验证
        registerParam.check(geetestLib, redisTemplate);
        //构建一个新的用户
        User user = getUser(registerParam);
        return false;
    }

    /**
     * 构建一个新的用户
     *
     * @return
     */
    private User getUser(RegisterParam registerParam) {
        String encodePwd = new BCryptPasswordEncoder().encode(registerParam.getPassword());
        User user = new User()
                .setCountryCode(registerParam.getCountryCode())
                .setEmail(registerParam.getEmail())
                .setMobile(registerParam.getMobile())
                .setPassword(encodePwd)
                .setPaypassSetting(false)
                .setStatus((byte) 1)
                .setType((byte) 1)
                .setAuthStatus((byte) 0)
                .setLogins(0)
                .setInviteCode(RandomUtil.randomString(6));
        if (!StringUtils.isEmpty(registerParam.getInvitionCode())) {
            User userPre = getOne(new LambdaQueryWrapper<User>().eq(User::getInviteCode, registerParam.getInvitionCode()));
            if (userPre != null) {
                user.setDirectInviteid(String.valueOf(userPre.getId())); // 邀请人的id , 需要查询
                user.setInviteRelation(String.valueOf(userPre.getId())); // 邀请人的id , 需要查询
            }
        }
        return user;
    }

    /**
     * 重置登陆密码
     *
     * @param unSetPasswordParam
     * @return
     */
    @Override
    public boolean unsetLoginPwd(UnSetPasswordParam unSetPasswordParam) {
        log.info("开始重置密码{}", JSON.toJSONString(unSetPasswordParam, true));
        //1、滑动验证
        unSetPasswordParam.check(geetestLib, redisTemplate);
        //2、手机号码验证
        String phoneValidateCode = stringRedisTemplate.opsForValue().get("SMS:FORGOT_VERIFY:" + unSetPasswordParam.getMobile());
        if (!unSetPasswordParam.getValidateCode().equals(phoneValidateCode)) {
            throw new IllegalArgumentException("手机验证码错误");
        }
        //3、检验数据库用户
        String mobile = unSetPasswordParam.getMobile();
        User user = getOne(new LambdaQueryWrapper<User>()
                .eq(User::getMobile, mobile));
        if (StringUtils.isEmpty(user)) {
            throw new IllegalArgumentException("该用户不存在");
        }
        String encode = new BCryptPasswordEncoder().encode(unSetPasswordParam.getPassword());
        user.setPassword(encode);
        return updateById(user);
    }

    /**
     * 通过用户的Id批量查询用户的基础信息
     *
     * @param ids 用户的id集合
     * @return
     */
    @Override
    public Map<Long, UserDto> getBasicUsers(List<Long> ids, String userName, String mobile) {
        if (CollectionUtils.isEmpty(ids) && StringUtils.isEmpty(userName) && StringUtils.isEmpty(mobile)){
            return Collections.emptyMap();
        }
        List<User> list = list(new LambdaQueryWrapper<User>()
                .in(!CollectionUtils.isEmpty(ids), User::getId, ids)
                .like(!StringUtils.isEmpty(userName), User::getUsername, userName)
                .like(!StringUtils.isEmpty(mobile), User::getMobile, mobile));
        if (CollectionUtils.isEmpty(list)){
            return Collections.emptyMap();
        }
        // 将user->userDto
        List<UserDto> userDtos = UserDtoMapper.INSTANCE.convert2Dto(list);
        Map<Long, UserDto> userDtoIdMappings = userDtos.stream().collect(Collectors.toMap(UserDto::getId, userDto -> userDto));
        return userDtoIdMappings;
    }
}
